package siam.ui;

import siam.core.MasterOfGame;
/**
 * class which handle event from board
 * @author Motassim Saad
 *
 */
public class EventControler {

	/**
	 * {@link SiamPanel}
	 */
	private SiamPanel pane;
	/**
	 * EventControler unique instance
	 */
	public static EventControler instance = new EventControler();
	/**
	 * {@link SiamFrame}
	 */
	private SiamFrame frame;
	/**
	 * {@see InactivePiecesPanel} represent the current inactivePiecesPanel
	 */
	private InactivePiecesPanel activePiecePanel;
	/**
	 * {@see ICase} represent the current activate case
	 */
	private ICase activateSiamCase;

	/**
	 * {@see MasterOfGame}
	 */
	private MasterOfGame mastergame = MasterOfGame.instance;
	/**
	 * count the force to try to move
	 */
	private int compteurForce = 0;

	private EventControler() {
	}

	/**
	 * {@link SiamPanel}
	 * 
	 * @param pane
	 */
	public void setPane(SiamPanel pane) {
		this.pane = pane;
	}

	/**
	 * Event Controller is notified when an {@link InactivePiecesPanel} was
	 * performed
	 * 
	 * @param inactivePiecesPanel
	 * @see InactivePiecesPanel
	 */
	synchronized public void notifyState(InactivePiecesPanel inactivePiecesPanel) {

		if (mastergame.getJoueurTurn().equals(inactivePiecesPanel.getJoueur())) {
			boolean putBack = false;
			// Si on avait deja selectionne une piece alors on la sort du jeu
			if (activateSiamCase != null) {
				Piece p = activateSiamCase.getPieces();

				if (p.getJoueur() != null) {
					putBack = true;
					p = activateSiamCase.popPiece();
					this.activateSiamCase.desactivate();
					activateSiamCase = null;
					if (p.getJoueur().equals(
							frame.getInactivePiecePanelJ1().getJoueur())) {
						frame.getInactivePiecePanelJ1().getListPieces().add(p);
						frame.getInactivePiecePanelJ1().repaint();
					} else {
						frame.getInactivePiecePanelJ2().getListPieces().add(p);
						frame.getInactivePiecePanelJ2().repaint();
					}
					mastergame.TurnFinish();
				}
			}
			if (this.activePiecePanel != null) {
				this.activePiecePanel.desactivate();
			}
			// sinon on active le panels
			if (!putBack) {
				this.activePiecePanel = inactivePiecesPanel;
				this.activePiecePanel.activate();
				this.pane.getForcePanel().setForceActivate(
						activePiecePanel.getListPieces().get(0).getForce());
			}
		}
	}

	/**
	 * Event Controller is notified when an {@link ICase} was performed
	 * 
	 * @param siamCase
	 * @see {@link SiamCase}
	 */
	synchronized public void notifyState(ICase siamCase) {

		// Si l'on souhaite placer une piece dans le jeu
		if (activePiecePanel != null && (siamCase.putableAtBegining())
				&& (siamCase.getPieces() == null)) {
			siamCase.put(this.activePiecePanel.getPiece());
			activePiecePanel.desactivate();
			activePiecePanel.repaint();
			activePiecePanel = null;
			pane.getForcePanel().setForceActivate(null);
			mastergame.TurnFinish();
			// si click inactive panel piece + click case non possible au debut
			// => deselection
		} else if (activePiecePanel != null && !(siamCase.putableAtBegining())) {
			activePiecePanel.desactivate();
			activePiecePanel.repaint();
			activePiecePanel = null;
			pane.getForcePanel().setForceActivate(null);
		}
		// placer une piece dans le jeu en poussant une autre
		else if (activePiecePanel != null && (siamCase.putableAtBegining())
				&& (siamCase.getPieces() != null)
				&& canPushAtBegining(siamCase)) {

			Piece p = activePiecePanel.getPiece();
			push(p, siamCase, p.getForce(), p.getJoueur());
			activePiecePanel.desactivate();
			activePiecePanel.repaint();
			activePiecePanel = null;
			pane.getForcePanel().setForceActivate(null);
			mastergame.TurnFinish();
			// clic successif sur deux siam case => deplacement
		} else if (isSuccessiveCases(siamCase)) {
			// si la pieces n'est pas nulle et que le dep est possible
			if (siamCase.getPieces() != null) {
				if (checkRules(siamCase)) {
					Piece p = activateSiamCase.popPiece();
					push(p, siamCase, p.getForce(), p.getJoueur());
					activateSiamCase.desactivate();
					activateSiamCase = null;
					pane.getForcePanel().setForceActivate(null);
					activePiecePanel = null;
					pane.getForcePanel().setForceActivate(null);
					pane.getForcePanel().setForceActivate(null);
					mastergame.TurnFinish();
				} else {
					mastergame.notifyImpossibleMove();
				}

			} else {
				siamCase.put(activateSiamCase.popPiece());
				activateSiamCase.desactivate();
				activateSiamCase = null;
				pane.getForcePanel().setForceActivate(null);
				pane.getForcePanel().setForceActivate(null);
				activePiecePanel = null;
				mastergame.TurnFinish();
			}

		} else if (activateSiamCase != null && !isSuccessiveCases(siamCase)) {
			activateSiamCase.desactivate();
			activateSiamCase = null;
			pane.getForcePanel().setForceActivate(null);
		}

		// Si l'on clique sur une autre case avec un animal

		else if (siamCase.getPieces() != null
				&& mastergame.getJoueurTurn() != null
				&& mastergame.getJoueurTurn().equals(
						siamCase.getPieces().getJoueur())) {
			if (activateSiamCase != null) {
				this.activateSiamCase.desactivate();
			}
			this.activateSiamCase = siamCase;
			this.pane.getForcePanel().setForceActivate(
					activateSiamCase.getPieces().getForce());
			siamCase.activate();
			if (activePiecePanel != null) {
				reInit(activePiecePanel);
				activePiecePanel = null;
			}

		}
		pane.repaint();
	}

	/**
	 * Return true if the case can received a pieces at the begining else false
	 * 
	 * @param {@link SiamCase} siamCase
	 * @return boolean
	 */
	private boolean canPushAtBegining(ICase siamCase) {
		Force force = activePiecePanel.getListPieces().get(0).getForce();
		compteurForce = 1;
		// Poussee et deplacement incompatible
		return isCompatiblePushAndForce(force, siamCase) && compteurForce > 0;

	}

	/**
	 *  Return true if all case are compatible with the {@link Force}
	 * @param {@link Force} force
	 * @param {@link ICase} dest
	 * @return boolean
	 */
	private boolean isCompatiblePushAndForce(Force force, ICase dest) {
		// tant que l'on a une case cible avec des pieces
		if (dest != null && dest.getPieces() != null) {
			Force other = dest.getPieces().getForce();
			// Si la force est nulle alors on est sur une pierre et relance le
			// processus avec la case suivante
			if (other == null && isInBoard(dest.getRow() + force.getDirI())
					&& isInBoard(dest.getColumn() + force.getDirJ())) {
				return isCompatiblePushAndForce(
						force,
						pane.getCase(dest.getRow() + force.getDirI(),
								dest.getColumn() + force.getDirJ()));
			}
			// Somme des forces oppose compteur--
			if (other != null
					&& other.equals(force.getOpposite())
					&& dest.getPieces().getJoueur() != mastergame
							.getJoueurTurn()) {
				compteurForce--;
				if (isInBoard(dest.getRow() + force.getDirI())
						&& isInBoard(dest.getColumn() + force.getDirJ())) {
					return isCompatiblePushAndForce(
							force,
							pane.getCase(dest.getRow() + force.getDirI(),
									dest.getColumn() + force.getDirJ()));
				}

			}
			// Un de nos animaux nous bloque
			else if (other != null
					&& other.equals(force.getOpposite())
					&& dest.getPieces().getJoueur() == mastergame
							.getJoueurTurn()) {
				return false;
			}
			// Force dans le meme sens compteur++++

			else if (other != null && other.equals(force)) {
				compteurForce++;
			}
			// tant que l'on peut continuer sur la ligne
			if (isInBoard(dest.getRow() + force.getDirI())
					&& isInBoard(dest.getColumn() + force.getDirJ())) {
				return isCompatiblePushAndForce(
						force,
						pane.getCase(dest.getRow() + force.getDirI(),
								dest.getColumn() + force.getDirJ()));
			}
		}
		return true;
	}
	/**
	 * Push the piece to dst {@link ICase}
	 * @param {@link Piece} src ,piece which be moved 
	 * @param {@link ICase} dst ,destination case
	 * @param {@link Force} force , force
	 * @param {@link Joueur} lastGood , represent the winner if Rocher is pushed
	 */
	private void push(Piece src, ICase dst, Force force, Joueur lastGood) {
		if (src != null) {
			Piece p = dst.popPiece();
			dst.put(src);
			//si le joueur est different et la force est la meme
			if (p != null && p.joueur != null && !p.joueur.equals(lastGood)&&p.getForce().equals(src.getForce())) {
				lastGood = p.joueur;
			}
			// si les indices ne sont plus sur le jeu
			if ((!isInBoard(dst.getRow() + force.getDirI()) || !isInBoard(dst
					.getColumn() + force.getDirJ()))) {
				// on replace dans le bon inactivePiecesPanel
				if (p != null) {
					if (p.getJoueur() == null) {
						mastergame.endGame(lastGood);
					} else {
						if (p.getJoueur().equals(
								frame.getInactivePiecePanelJ1().getJoueur())) {
							frame.getInactivePiecePanelJ1().getListPieces()
									.add(p);
							frame.getInactivePiecePanelJ1().repaint();
						} else {
							frame.getInactivePiecePanelJ2().getListPieces()
									.add(p);
							frame.getInactivePiecePanelJ2().repaint();
						}
					}
				}
				pane.repaint();
				// sinon on continue a deplacer les case
			} else {
				push(p,
						pane.getCase(dst.getRow() + force.getDirI(),
								dst.getColumn() + force.getDirJ()), force,
						lastGood);
			}
		}

	}

	/**
	 * Return true if the indices are in the Board game
	 * @param i inde
	 * @return boolean
	 */
	private boolean isInBoard(int i) {
		return i >= 0 && i <= pane.getNUMBER_PIECES() - 1;
	}

	/**
	 * Return true if the Case can be push by another piece at the begining
	 * @param siamCase
	 * @return boolean
	 * @see {@link #isCompatiblePushAndForce(Force, ICase)}
	 */
	private boolean checkRules(ICase siamCase) {
		Force force = activateSiamCase.getPieces().getForce();
		compteurForce = 1;
		// Poussee et deplacement incompatible
		if (!pane.getCase(activateSiamCase.getRow() + force.getDirI(),
				activateSiamCase.getColumn() + force.getDirJ())
				.equals(siamCase)) {
			return false;
		}
		return isCompatiblePushAndForce(force, siamCase) && compteurForce > 0;
	}

	private void reInit(ICustomJComponent compo) {
		compo.desactivate();
		compo.repaint();
	}

	/**
	 * Return true if the siam case is next to  {@link #activateSiamCase}
	 * @param {@link ICase} siamCase
	 * @return boolean
	 */
	private boolean isSuccessiveCases(ICase siamCase) {
		if (this.activateSiamCase != null
				&& siamCase.isNextTo(activateSiamCase)) {
			return activateSiamCase.getPieces() != null;
		}
		return false;
	}

	public void setFrame(SiamFrame siamFrame) {
		this.frame = siamFrame;
	}
	/**
	 * Event Controller is notified when an {@link ForcePanel} was performed and change force if case is selected
	 * @param {@link Force} force
	 */
	public void notifyForce(Force force) {
		// on change la force de l'animal sur un plateau
		if (activateSiamCase != null
				&& !activateSiamCase.getPieces().getForce().equals(force)) {
			activateSiamCase.getPieces().setForce(force);
			this.pane.getForcePanel().setForceActivate(
					activateSiamCase.getPieces().getForce());
			activateSiamCase.desactivate();
			activateSiamCase = null;
			mastergame.TurnFinish();
			pane.getForcePanel().setForceActivate(null);
			pane.repaint();
			// on change force animal a placer
		} else if (activePiecePanel != null) {
			activePiecePanel.getListPieces().get(0).setForce(force);
			this.pane.getForcePanel().setForceActivate(
					activePiecePanel.getListPieces().get(0).getForce());
		}
	}

}
